﻿using Infrastructure.Utils.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;

namespace Extended.WPF.Core.Helpers
{
    public static class DependencyPropertyHelper2
    {
        volatile static bool methodsCreated = false;
        static readonly object lockObject = new object();
        static Func<object, object> getSynchronized;
        static Func<object, object> get_metadataMap;
        static Action<object, int, object> set_Item;
        static Func<object, int, object> get_Item;
        static Action<object, object> set_propertyChangedCallback;
        static void CheckMethodsCreated(DependencyProperty property)
        {
            if (methodsCreated)
                return;
            lock (lockObject)
            {
                if (methodsCreated)
                    return;
                var dPropType = typeof(DependencyProperty);
                getSynchronized = ReflectionHelper.CreateFieldGetter<object, object>(dPropType, "Synchronized", BindingFlags.Static | BindingFlags.NonPublic);
                get_metadataMap = ReflectionHelper.CreateFieldGetter<object, object>(dPropType, "_metadataMap", BindingFlags.NonPublic | BindingFlags.Instance);
                var mmap = get_metadataMap(property);
                set_Item = ReflectionHelper.CreateInstanceMethodHandler<Action<object, int, object>>(mmap, "set_Item", BindingFlags.Public | BindingFlags.Instance, mmap.GetType(), false, null, typeof(object));
                get_Item = ReflectionHelper.CreateInstanceMethodHandler<Func<object, int, object>>(mmap, "get_Item", BindingFlags.Public | BindingFlags.Instance, mmap.GetType(), false, null, typeof(object));
                set_propertyChangedCallback = ReflectionHelper.CreateFieldSetter<object, object>(typeof(PropertyMetadata), "_propertyChangedCallback", BindingFlags.Instance | BindingFlags.NonPublic);
                methodsCreated = true;
            }
        }
        public static void UnOverrideMetadata(this DependencyProperty property, Type ownerType)
        {
            CheckMethodsCreated(property);
            lock (getSynchronized(null))
                set_Item(get_metadataMap(property), DependencyObjectType.FromSystemType(ownerType).Id, DependencyProperty.UnsetValue);
        }
        public static bool IsMetadataOverriden(this DependencyProperty property, Type ownerType)
        {
            CheckMethodsCreated(property);
            lock (getSynchronized(null))
                return get_Item(get_metadataMap(property), DependencyObjectType.FromSystemType(ownerType).Id) != DependencyProperty.UnsetValue;
        }
        public static void AddPropertyChangedCallback(this DependencyProperty property, Type ownerType, PropertyChangedCallback callback)
        {
            if (!property.IsMetadataOverriden(ownerType))
            {
                property.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(callback));
            }
            else
            {
                var metadata = property.GetMetadata(ownerType);
                lock (metadata)
                {
                    var newCallback = metadata.PropertyChangedCallback == null ? callback : Delegate.Combine(metadata.PropertyChangedCallback, callback);
                    set_propertyChangedCallback(metadata, newCallback);
                }
            }
        }
    }
}
